#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include "util_gpio.h"

#define NUM2CHAR(n) ((n) + '0')
#define CHAR2NUM(c) ((c) - '0')

int gpio_export(unsigned int gpio_num)
{
	char *path = "/sys/class/gpio/export";
	int fd = -1, ret;
	char buff[16] = {0};

	if (access((const char *)path, F_OK) < 0)
	{
		fprintf(stderr, "util gpio : %s not exist\n", path);
		return -1;
	}

	fd = open(path, O_WRONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}
	sprintf(buff, "%d", gpio_num);
	ret = write(fd, buff, strlen(buff));
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : write %s failed\n", path);
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

int gpio_unexport(unsigned int gpio_num)
{
	char *path = "/sys/class/gpio/unexport";
	int fd = -1, ret;
	char buff[16] = {0};

	if (access((const char *)path, F_OK) < 0)
	{
		fprintf(stderr, "util gpio : %s not exist\n", path);
		return -1;
	}

	fd = open(path, O_WRONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}
	sprintf(buff, "%d", gpio_num);
	ret = write(fd, buff, strlen(buff));
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : write %s failed\n", path);
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

int gpio_set_direction(unsigned int gpio_num, DIR_E dir)
{
	assert(dir >= 0 && dir <= 1);
	int fd = -1, ret;
	char path[64] = {0};
	char *dir_names[] = {"out", "in"};

	sprintf(path, "/sys/class/gpio/gpio%d/direction", gpio_num);
	if (access((const char *)path, F_OK) < 0)
	{
		fprintf(stderr, "util gpio : %s not exist\n", path);
		return -1;
	}

	fd = open(path, O_WRONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}
	ret = write(fd, dir_names[dir], strlen(dir_names[dir]));
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : write %s failed\n", path);
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

int gpio_read(unsigned int gpio_num)
{
	int fd = -1;
	char path[64], val[8];
	int ret = 0;

	sprintf(path, "/sys/class/gpio/gpio%d/value", gpio_num);
	if (access((const char *)path, F_OK) < 0)
	{
		fprintf(stderr, "util gpio : %s not exist\n", path);
		return -1;
	}
	fd = open(path, O_RDONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}
	memset(val, 0, sizeof(val));
	ret = read(fd, val, 1);
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : read %s failed\n", path);
		close(fd);
		return -1;
	}
	close(fd);
	ret = CHAR2NUM(val[0]);
	return ret;
}

int gpio_write(unsigned int gpio_num, int state)
{
	assert(state >= 0 && state <= 1);
	int fd = -1, ret;
	char path[64], val[4];

	sprintf(path, "/sys/class/gpio/gpio%d/value", gpio_num);
	fd = open(path, O_WRONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}

	memset(val, 0, sizeof(val));
	val[0] = NUM2CHAR(state);
	ret = write(fd, val, 1);
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : write %s failed\n", path);
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

int gpio_set_edge(unsigned int gpio_num, EDGE_E mode)
{
	assert(mode >= 0 && mode <= 3);
	int fd, ret;
	char path[64];
	const char *trigger[4] = {"none", "rising", "falling", "both"};

	sprintf(path, "/sys/class/gpio/gpio%d/edge", gpio_num);
	fd = open(path, O_WRONLY);
	if (fd < 0)
	{
		fprintf(stderr, "util gpio : open %s failed\n", path);
		return -1;
	}

	ret = write(fd, trigger[mode], sizeof(trigger[mode]));
	if (ret < 0)
	{
		fprintf(stderr, "util gpio : write %s failed\n", path);
		close(fd);
		return -1;
	}

	close(fd);
	return 0;
}